/**
 * https://www.tslang.cn/docs/handbook/generics.html
 * 泛型
 */

// 软件工程中, 我们不仅要创建一致的定义良好的API, 同时也要考虑可重用性。
// 组件不仅能够支持当前的数据类型, 同时也能支持未来的数据类型, 
// 这在创建大型系统时为你提供了十分灵活的功能。

// 在像C#和Java这样的语言中, 可以使用“泛型”来创建可重用的组件, 
// 一个组件可以支持多种类型的数据。
// 这样用户就可以以自己的数据类型来使用组件。

function identity101(arg: number): number {
    return arg;
}

/**
 * 使用`any`会导致这个函数可以接收任何类型的`arg`参数, 这样就丢失了一些信息: 
 * 传入的类型与返回的类型应该是相同的。
 * 如果我们传入一个数字, 我们只知道任何类型的值都有可能被返回。
 * 
 * @param arg 
 */
function identity102(arg: any): any {
    return arg;
}

/**
 * 因此, 我们需要一种方法使返回值的类型与传入参数的类型是相同的。
 * 这里, 我们使用了“类型变量”, 它是一种特殊的变量, 只用于表示类型而不是值。
 * 
 * 我们给`identity`添加了类型变量`T`。`T`帮助我们捕获用户传入的类型,
 * 之后我们就可以使用这个类型。
 * 现在我们可以知道参数类型与返回值类型是相同的了。
 * 这允许我们跟踪函数里使用的类型信息。
 * 
 * @param arg 
 * 
 */
function identity<T>(arg: T): T {
    return arg;
}

let output101 = identity<string>("myString");

let output102 = identity("myString");

function loggingIdentity101<T>(arg: T[]) : T[] {
    console.log( arg.length ); // Array has a .length so no more error
    return arg;
}

function loggingIdentity102<T>(arg: Array<T>): Array<T> {
    console.log(arg.length);
    return arg;
}

function identity103<T>(arg:T):T{
    return arg;
}
let myIdentity103: <T>(arg: T) => T = identity103;

function identity104<T>(arg: T): T {
    return arg;
}
let myIdentity104: <U>(arg: U) => U = identity104;

function identity105<T>(arg: T): T {
    return arg;
}
let myIdentity105: {<T>(arg:T):T} = identity105;

interface GenericIdentityFn106 {
    <T>(arg: T): T;
}
function identity106<T>(arg:T):T{
    return arg;
}
let myIdentity106: GenericIdentityFn106 = identity106;

interface GenericIdentityFn107<T> {
    (arg: T): T;
}
function identity107<T>(arg:T):T{
    return arg;
}
let myIdentity107: GenericIdentityFn107<number> = identity107;

class GenericNumber101<T> {
    zeroValue: T;
    add: (x:T, y:T) => T;
}
let myGenericNumber101 = new GenericNumber101<number>();
myGenericNumber101.zeroValue = 0;
myGenericNumber101.add = function(x,y){return x+y;};

let stringNumeric101 = new GenericNumber101<string>();
stringNumeric101.zeroValue = "";
stringNumeric101.add = function(x,y){return x+y;};

alert(stringNumeric101.add(stringNumeric101.zeroValue, "test"));

interface Lengthwise {
    length: number;
}
function loggingIdentity103<T extends Lengthwise>(arg: T): T {
    console.log(arg.length); // Now we know it has a .length property, so no more error
    return arg;
}
// loggingIdentity103(3) // Error, number doesn't have a .length property
loggingIdentity103({length:10, value: 3});

/**
 * https://www.tslang.cn/docs/handbook/generics.html
 * 在泛型约束中使用类型参数
 * 
 * 例子有错,无法在"TypeScript2.7.2"下运行
 */

// interface myInf{
//     [index: number]: any;
// }
// function getProperty101<T extends myInf>(obj: T, key: K){
//     return obj[key];
// }

function create<T>(c: {new(): T;}): T {
    return new c();
}

class BeeKeeper {
    hasMask: boolean;
}

class ZooKeeper {
    nametag: string;
}

class Animal {
    numLegs: number;
}

class Bee extends Animal {
    keeper: BeeKeeper;
}

class Lion extends Animal {
    keeper: ZooKeeper;
}

function createInstance<A extends Animal>(c: new () => A): A {
    return new c();
}

createInstance(Lion).keeper.nametag; // typechecks!
createInstance(Bee).keeper.hasMask; // typechecks!



